package com.shuwei.wificollecttool.recognize;

import com.shuwei.location.wificollect.WifiItem;
import com.shuwei.wificollecttool.recognize.dao.FingerPrint9KindDao;
import com.shuwei.wificollecttool.recognize.dao.IFingerPrintDao;

import java.util.ArrayList;
import java.util.List;

public class FindFingerPrint9Kind {
    public static void main(String[] args) {
        FindFingerPrint9Kind findFingerPrint = new FindFingerPrint9Kind();
        String[] test_AP = {"50:fa:84:48:95:66", "8c:a6:df:c8:f3:cc", "d0:ee:07:1d:84:2c", "8c:a6:df:c8:f3:ca", "38:e2:dd:22:41:b0", "34:96:72:6c:2d:86", "d4:ee:07:1d:84:2c", "a8:ad:3d:2c:a4:7c", "fc:37:2b:62:26:c9", "88:25:93:bd:ef:8b", "8c:c7:d0:0b:f2:11", "d4:ee:07:3f:17:6e", "b0:d5:9d:81:e5:41", "50:fa:84:48:8b:1c", "00:87:36:01:4b:ca", "50:fa:84:48:8b:1a", "cc:79:cf:c0:6c:0a", "50:fa:84:48:95:68", "b0:d5:9d:6e:1e:98", "96:53:30:70:79:bd", "64:27:37:6d:46:8b", "d6:ee:07:3e:72:49", "d0:ee:07:3f:17:6e", "06:1b:b1:22:88:40", "e0:10:7f:53:f4:b8", "06:02:b4:49:aa:1f", "8c:c7:d0:0b:f2:10", "d6:ee:07:37:32:1d", "e0:10:7f:13:f4:b8", "06:1b:b1:22:6d:f9", "fc:37:2b:4b:9b:99", "a4:dc:be:f4:f1:00"
        };
        int[] test_RSSI = {-44, -39, -53, -43, -41, -38, -39, -45, -53, -45, -34, -50, -56, -64, -62, -63, -61, -57, -64, -64, -80, -53, -69, -75, -62, -67, -51, -76, -60, -72, -73, -89
        };
        List<WifiItem> currWifiItemList = new ArrayList<>();
        for (int i = 0; i < test_AP.length; i++) {
            WifiItem wifiItem = new WifiItem();
            String mac = test_AP[i];
            int rssi = test_RSSI[i];
            wifiItem.setRssi(rssi);
            wifiItem.setBssid(mac);
            currWifiItemList.add(wifiItem);
        }
        findFingerPrint.locationHit(currWifiItemList);
    }

    public void locationHit(List<WifiItem> currWifiItemList) {
        if (currWifiItemList==null || currWifiItemList.size() <=0) {
            return;
        }
        IFingerPrintDao iFingerPrintDao = new FingerPrint9KindDao();
        List<List<WifiItem>> lists = iFingerPrintDao.queryAllFingerPrint();
        final int currSize = currWifiItemList.size();
        final int eachDbDataSize = lists.size();

        List<Double> distance0 = new ArrayList<>();
        List<Double> distance1 = new ArrayList<>();
        List<Double> distance2 = new ArrayList<>();
        List<Double> distance3 = new ArrayList<>();
        List<Double> distance4 = new ArrayList<>();
        List<Double> distance5 = new ArrayList<>();
        List<Double> distance6 = new ArrayList<>();
//        List<Double> distance7 = new ArrayList<>();
        List<Double> distance8 = new ArrayList<>();
        List<Double> distance9 = new ArrayList<>();

        for (int i = 0; i < eachDbDataSize; i++) {
            List<WifiItem> wifiItems = lists.get(i);
            int eachDbDataSonSize = wifiItems.size();

            List<Integer> diff_value_text2figer = new ArrayList<>();
            List<Integer> finger_RSSI_tem = new ArrayList<>();
            List<String> finger_AP_tem = new ArrayList<>();
            List<Integer> test_RSSI_tem = new ArrayList<>();
            List<String> test_AP_tem = new ArrayList<>();
//            List<Double> diff_value_text2figersort = new ArrayList<>();
            List<Integer> point_muti = new ArrayList<>();
            List<Double> Jff_value = new ArrayList<>();
            List<Integer> lan = new ArrayList<>();

            for (int j = 0; j < eachDbDataSonSize; j++) {
                for (int k = 0; k < currSize; k++) {
                    WifiItem currWifiItem = currWifiItemList.get(k);
                    WifiItem dbWifiItem = lists.get(i).get(j);
                    //test的AP在指纹中能找到,称为识别
                    if (dbWifiItem.getBssid().equals(currWifiItem.getBssid())) {
                        finger_RSSI_tem.add(dbWifiItem.getRssi());
                        finger_AP_tem.add(dbWifiItem.getBssid());
                        test_RSSI_tem.add(currWifiItem.getRssi());
                        test_AP_tem.add(currWifiItem.getBssid());
                        //统计后差异值
                        int absTmp = Math.abs(currWifiItem.getRssi() - dbWifiItem.getRssi());
                        diff_value_text2figer.add(absTmp);
                        //排序start
//                        diff_value_text2figersort.addAll(diff_value_text2figer);
                        /*Collections.sort(diff_value_text2figersort, new Comparator<Double>() {
                            @Override
                            public int compare(Double o1, Double o2) {
                                if (o1.doubleValue() > o2.doubleValue()) {
                                    return -1;
                                } else if(o1.doubleValue() < o2.doubleValue()) {
                                    return 1;
                                } else {
                                    return 0;
                                }
                            }
                        });
                        //两种写法等效
                        */
                        /*Collections.sort(diff_value_text2figersort, (o1, o2) -> {
                            if (o1.doubleValue() > o2.doubleValue()) {
                                return -1;
                            } else if(o1.doubleValue() < o2.doubleValue()) {
                                return 1;
                            } else {
                                return 0;
                            }
                        });*/
                        //排序end
                        //xi*yi
                        final Integer dbItemRssi = dbWifiItem.getRssi();
                        final Integer currItemRssi = currWifiItem.getRssi();
                        point_muti.add(currItemRssi.intValue() * dbItemRssi.intValue());
                        //J&M距离计算
                        int absDbItemRssi = Math.abs(dbItemRssi);
                        int absCurrItemRssi = Math.abs(currItemRssi);
                        double powAbsDbItemRssi = Math.pow(absDbItemRssi, 0.5);
                        double powAbsCurrItemRssi = Math.pow(absCurrItemRssi, 0.5);
                        double pow = Math.pow(powAbsDbItemRssi - powAbsCurrItemRssi, 2);
                        Jff_value.add(pow);
                        //lan式距离计算
                        int absSumCurrItemRssiDbItemRssi = Math.abs(currItemRssi) + Math.abs(dbItemRssi);
                        lan.add(absSumCurrItemRssiDbItemRssi);
                    }
                }//k end
            }//j end
            //%%%方法1 直接向量每个分量的差值求和，取平均，为了减小识别数目过多引起的累加误差,越小越接近
            //%%%进化曼哈顿距离
            int diff_value_text2figer_size = diff_value_text2figer.size();
            double fun0Distance = ((double) sumInt(diff_value_text2figer))/ diff_value_text2figer_size;
            distance0.add(fun0Distance);
            //%%%方法2 取前面几个分量，直接向量每个分量的差值求和，取平均，为了减小识别数目过多引起的累加误差，越小越接近
            double distance1TmpSum = sumInt(diff_value_text2figer, 0, diff_value_text2figer.size() - 1 - 3);
            distance1.add(distance1TmpSum / (diff_value_text2figer_size - 3));
            //%%%方法3 欧式距离，越大越接近
            double multiplicationSum = multiplicationSum(diff_value_text2figer);
            double fun2Tmp = Math.pow(multiplicationSum, 0.5) / diff_value_text2figer_size;
            distance2.add(1 / (1 + fun2Tmp));
            //%%%方法4 对每个分量求概率，当临近时投0.7中等投0.2远投0.1，越大越接近
            double pro = 0;
            double p = 0;
            for (int q = 0; q < diff_value_text2figer_size; q++) {
                Integer item = diff_value_text2figer.get(q);
                if (item.intValue() < 6) {
                    p = 0.7;
                } else if (item.intValue() < 10 && item.intValue() >= 6) {
                    p = 0.2;
                } else {
                    p = 0.1;
                }
                pro = pro + p;
            }
            double fun3Tmp = pro / diff_value_text2figer_size;
            distance3.add(fun3Tmp);
            //%%%方法5 对每个分量在出现距离这个维度投票近，中，远，越大越接近
            double num6 = 0;
            double num12 = 0;
            double num20 = 0;
            double num21 = 0;
            for (int q=0;q<diff_value_text2figer_size;q++) {
                double fun4Tmp = diff_value_text2figer.get(q).doubleValue();
                if (fun4Tmp <= 6) {
                    num6 = num6 + 1;
                } else if (fun4Tmp < 10 && fun4Tmp >= 6) {
                    num12 = num12 + 1;
                } else if (fun4Tmp < 20) {
                    num20 = num20 + 1;
                } else {
                    num21 = num21 + 1;
                }
            }
            double fun4Tmp2 = num6 / (num6 + num12 + num20 + num21) * 0.6
                    + num12 / (num6 + num12 + num20 + num21) * 0.25
                    + num20 / (num6 + num12 + num20 + num21) * 0.15;
            distance4.add(fun4Tmp2);
            //%%%方法6 consine相似度
            int sumPointMuti = sumInt(point_muti);
            double powBySum_finger_RSSI_tem = powBySum(finger_RSSI_tem, 2);
            double multiplicationSum_test_RSSI_tem = multiplicationSum(test_RSSI_tem);
            double fun5PowTmp = Math.pow(powBySum_finger_RSSI_tem * multiplicationSum_test_RSSI_tem, 0.5);
            double fun5Tmp = sumPointMuti / fun5PowTmp;
            distance5.add(fun5Tmp);
            //%%%方法7 tanimoto系数
//            int sumPointMuti = sum(point_muti).intValue();
            double multiplicationSum_finger_RSSI_tem = multiplicationSum(finger_RSSI_tem);
            double pow_multiplicationSum_finger_RSSI_tem = Math.pow(multiplicationSum_finger_RSSI_tem, 0.5);
//            double multiplicationSum_test_RSSI_tem = multiplicationSum(test_RSSI_tem);
            double pow_multiplicationSum_test_RSSI_tem = Math.pow(multiplicationSum_test_RSSI_tem, 0.5);
            double fun6Tmp = sumPointMuti / (pow_multiplicationSum_finger_RSSI_tem
                    + pow_multiplicationSum_test_RSSI_tem
                    - sumPointMuti);
            distance6.add(fun6Tmp);
            //%%%方法8 马氏距离
            //%%%方法9 j&m 距离
            double sum = sumDouble(Jff_value);
            double fun8Tmp = Math.pow(sum, 0.5);
            distance8.add(fun8Tmp);
            //%%%方法10 兰式距离
            double fun9Tmp = divideSum(diff_value_text2figer, lan);
            distance9.add(fun9Tmp);
        }//i end
        getHitIndex(LocationListener.FUN_INDEX_0, distance0, false, this.locationListener);
        getHitIndex(LocationListener.FUN_INDEX_1, distance1, false, this.locationListener);
        getHitIndex(LocationListener.FUN_INDEX_2, distance2, true, this.locationListener);
        getHitIndex(LocationListener.FUN_INDEX_3, distance3, true, this.locationListener);
        getHitIndex(LocationListener.FUN_INDEX_4, distance4, true, this.locationListener);
        getHitIndex(LocationListener.FUN_INDEX_5, distance5, true, this.locationListener);
        getHitIndex(LocationListener.FUN_INDEX_6, distance6, true, this.locationListener);
//      = getHitIndex(LocationListener.FUN_INDEX_7, distance7, true, this.locationListener);
        getHitIndex(LocationListener.FUN_INDEX_8, distance8, false, this.locationListener);
        getHitIndex(LocationListener.FUN_INDEX_9, distance9, false, this.locationListener);


        //for test
//        System.out.println("y="+maxOrMinIndex0);
//        System.out.println("y2="+maxOrMinIndex1);
//        System.out.println("y3="+maxOrMinIndex2);
//        System.out.println("y4="+maxOrMinIndex3);
//        System.out.println("y5="+maxOrMinIndex4);
//        System.out.println("y6="+maxOrMinIndex5);
//        System.out.println("y7="+maxOrMinIndex6);
////        System.out.println("y8="+maxOrMinIndex7);
//        System.out.println("y9="+maxOrMinIndex8);
//        System.out.println("y10="+maxOrMinIndex9);
    }

    private void getHitIndex(int funIndex, List<Double> list, boolean isGetMax, LocationListener locationListener) {
        if (isGetMax) {
            int maxIndex = 0;
            double maxValue = list.get(maxIndex).doubleValue();
            int progressIndex = 0;
            for (int i = 1; i < list.size(); i++) {
                try {
                    double val = list.get(i).doubleValue();
                    if (maxValue < val) {
                        maxIndex = i;
                        maxValue = val;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                progressIndex = i;
            }
            if (locationListener != null) {
                locationListener.locationHit(funIndex, "", maxIndex);
            }
        } else {
            int minIndex = 0;
            double minValue = list.get(minIndex).doubleValue();
            for (int i = 1; i < list.size(); i++) {
                double val = list.get(i).doubleValue();
                if (minValue > val) {
                    minIndex = i;
                    minValue = val;
                }
            }
            if (locationListener != null) {
                locationListener.locationHit(funIndex, "", minIndex);
            }
        }
    }

    private double divideSum(List<Integer> list0, List<Integer> list1) {
        double sum = 0;
        for (int i = 0; i < list0.size(); i++) {
            try {
                double item0 = list0.get(i).doubleValue();
                int item1 = list1.get(i).intValue();
                sum += item0 / item1;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return sum;
    }

    private <T> double powBySum(List<T> list, double multiBy) {
        if (list == null || list.size() <= 0) {
            return 0;
        }
        double sum = 0;
        for (int i = 0; i < list.size(); i++) {
            Object item = list.get(i);
            double v;
            if (item instanceof Double) {
                v = ((Double)item).doubleValue();
            } else if(item instanceof Integer) {
                v = ((Integer)item).intValue();
            }else {
                v = 0;
            }
            sum += Math.pow(v,multiBy);
        }
        return sum;
    }

    private <T> double multiplicationSum(List<T> list) {
        if (list == null || list.size() <= 0) {
            return 0;
        }
        double sum = 0;
        for (int i = 0; i < list.size(); i++) {
            Object item = list.get(i);
            double v;
            if (item instanceof Double) {
                v = ((Double)item).doubleValue();
            } else if(item instanceof Integer) {
                v = ((Integer)item).intValue();
            }else {
                v = 0;
            }
            sum += v * v;
        }
        return sum;
    }

    private double sumDouble(List<Double> list) {
        if (list == null || list.size() <= 0) {
            return 0;
        }
        double sumDouble = 0;
        for (int i = 0; i < list.size(); i++) {
            Double item = list.get(i);
            sumDouble += item.doubleValue();
        }
        return sumDouble;
    }

    private int sumInt(List<Integer> list) {
        if (list == null || list.size() <= 0) {
            return 0;
        }
        int sum = 0;
        for (int i = 0; i < list.size(); i++) {
            Integer item = list.get(i);
            sum += item.intValue();
        }
        return sum;
    }

    /**
     * 包含前包含后
     * @param list
     * @param includeStartIndex
     * @param includeEndIndex
     * @return
     */
    private double sumInt(List<Integer> list,int includeStartIndex, int includeEndIndex) {
        if (list == null || list.size() <= 0) {
            return 0;
        }
        List<Integer> doubles = list.subList(includeStartIndex, includeEndIndex + 1);
        double sum = 0;
        for (int i = 0; i < doubles.size(); i++) {
            Integer item = doubles.get(i);
            sum += item.doubleValue();
        }
        return sum;
    }

    private double sumDouble(List<Double> list,int startIndex, int endIndex) {
        if (list == null || list.size() <= 0) {
            return 0;
        }
        List<Double> doubles = list.subList(startIndex, endIndex);
        double sum = 0;
        for (int i = 0; i < doubles.size(); i++) {
            Double item = doubles.get(i);
            sum += item.doubleValue();
        }
        return sum;
    }

    private LocationListener locationListener;

    public void setLocationListener(LocationListener locationListener) {
        this.locationListener = locationListener;
    }

    public interface LocationListener {

        /**
         * 不同算法的index
         */
        int FUN_INDEX_0 = 0;
        int FUN_INDEX_1 = 1;
        int FUN_INDEX_2 = 2;
        int FUN_INDEX_3 = 3;
        int FUN_INDEX_4 = 4;
        int FUN_INDEX_5 = 5;
        int FUN_INDEX_6 = 6;
        //        int FUN_INDEX_7 = 7;
        int FUN_INDEX_8 = 8;
        int FUN_INDEX_9 = 9;

        void locationHit(int funIndex, String msg, int hitIndex);
    }
}